/*******************************************************************************
** Copyright (c) 2004 MaK Technologies, Inc.
** All rights reserved.
*******************************************************************************/
/*******************************************************************************
** $RCSfile: simple13.cxx,v $ $Revision: 1.5 $ $State: Exp $
*******************************************************************************/

// A simple federate that updates an object with attributes whose values
// are the name of the attribute. Objects from other simple federates
// are discovered and reflected. The string values are byte encoded to allow
// compatiblity between 1.3 and 1516

#pragma warning(disable: 4786)
#pragma warning(disable: 4290)

#ifdef WIN32
#include <winsock2.h>
#include <process.h>
#else
#include <unistd.h>
#endif

#include <stdio.h>

#include <map>
#include <string>
#include <iostream>
#include <sstream>

#include "RTI.hh"
#include "fedAmb13.h"
#include "keyboard.h"

using namespace std;

// Handles keyboard input without blocking
keyboard input;

// Map between strings and attribute handles
typedef std::map<std::string, RTI::AttributeHandle>  DtAttrNameHandleMap;

// Map between strings and parameter handles
typedef std::map<std::string, RTI::ParameterHandle>  DtParamNameHandleMap;

// Data shared between federate and federate ambassador
DtTalkAmbData theAmbData;

// The object class name
std::string theClassName = "BaseEntity";

// The interaction class name
std::string theInterClassName = "WeaponFire";

// The object class handle (to be retrieved from RTI).
RTI::ObjectClassHandle theClassHandle;

// The interaction class handle (to be retrieved from RTI).
RTI::InteractionClassHandle theInterClassHandle;

// The object instance handle (to be retrieved from the RTI).
RTI::ObjectHandle theObjectHandle;

// Map between strings and attribute handles
DtAttrNameHandleMap theAttrNameHandleMap;

// Map between strings and parameter handles
DtParamNameHandleMap theParamNameHandleMap;

////////////////////////////////////////////////////////////////////////////////
// Create the federation execution
void createFedEx(RTI::RTIambassador & rtiAmb,
                 std::string const& fedName,
                 std::string const& fedFile)
{
   cout  << "createFederationExecution "
         << fedName.c_str() << " "
         << fedFile.c_str() << endl;
   try
   {
      rtiAmb.createFederationExecution(fedName.c_str(), fedFile.c_str());
   }
   catch(RTI::FederationExecutionAlreadyExists& ex)
   {
      cout  << "Could not create Federation Execution: "
            << "FederationExecutionAlreadyExists: "
            <<  ex._name << " "
            << ex._reason << endl;
   }
   catch(RTI::Exception& ex)
   {
      cout  << "Could not create Federation Execution: " << endl
            << "RTI Exception: "
            << ex._name << " "
            << ex._reason << endl;
      exit(0);
   }
   rtiAmb.tick(0.1, 0.2);

   cout << "Federation Created" << endl;
}

////////////////////////////////////////////////////////////////////////////////
// Join the federation execution
void joinFedEx(
   RTI::RTIambassador & rtiAmb, MyFederateAmbassador* fedAmb,
   std::string const& federateType, std::string const& federationName)
{
   bool joined=false;
   const int maxTry  = 10;
   int numTries = 0;
   cout  << "joinFederationExecution "
         << federateType.c_str() << " "
         << federationName.c_str() << endl;

   while (!joined && numTries++ < maxTry)
   {
      try
      {
         rtiAmb.joinFederationExecution(federateType.c_str(),
            federationName.c_str(), fedAmb);
         joined = true;
      }
      catch(RTI::FederationExecutionDoesNotExist)
      {
         cout  << "FederationExecutionDoesNotExist, try "
               << numTries << "out of "
               << maxTry << endl;
         continue;
      }
      catch(RTI::Exception& ex)
      {
         cout  << "RTI Exception: "
               << ex._name << " "
               << ex._reason << endl;
         return;
      }
      rtiAmb.tick(0.1, 0.2);
   }
   if (joined)
      cout << "Joined Federation." << endl;
   else
   {
      cout << "Giving up." << endl;
      rtiAmb.destroyFederationExecution(federationName.c_str());
      exit(0);
   }
}

////////////////////////////////////////////////////////////////////////////////
// Resign and destroy the federation execution
void resignAndDestroy( RTI::RTIambassador & rtiAmb,
      std::string const& federationName)
{
   rtiAmb.resignFederationExecution(RTI::DELETE_OBJECTS);
   rtiAmb.destroyFederationExecution(federationName.c_str());
}

////////////////////////////////////////////////////////////////////////////////
// Publish and subscribe the object class attributes.
// Register an object instance of the class.
bool publishSubscribeAndRegisterObject(RTI::RTIambassador & rtiAmb)
{
   // Get the object class handle
   try
   {  
      theClassHandle = rtiAmb.getObjectClassHandle(theClassName.c_str());
      theAmbData.objectClassMap[theClassHandle] = theClassName;
   }
   catch (RTI::Exception& ex)
   {
      cout  << "RTI Exception: "
            << ex._name << " "
            << ex._reason << endl
            << "Could not get object class handle: "
            << theClassName.c_str() << endl;
      return false;
   }

   // Get the attribute handles and construct the name-handle map
   std::string attrName;
   try
   {
      attrName  = "AccelerationVector";
      theAttrNameHandleMap[attrName] = 
            rtiAmb.getAttributeHandle(attrName.c_str(), theClassHandle);
      attrName  = "VelocityVector";
      theAttrNameHandleMap[attrName] = 
            rtiAmb.getAttributeHandle(attrName.c_str(), theClassHandle);
      attrName  = "Orientation";
      theAttrNameHandleMap[attrName] = 
            rtiAmb.getAttributeHandle(attrName.c_str(), theClassHandle);
      attrName  = "DeadReckoningAlgorithm";
      theAttrNameHandleMap[attrName] = 
            rtiAmb.getAttributeHandle(attrName.c_str(), theClassHandle);
      attrName  = "WorldLocation";
      theAttrNameHandleMap[attrName] = 
            rtiAmb.getAttributeHandle(attrName.c_str(), theClassHandle);
   }
   catch (RTI::Exception& ex)
   {
      cout  << "RTI Exception: "
            << ex._name << " "
            << ex._reason << endl
            << "Could not get attribute handle "
            << attrName.c_str() << endl;
      return false;
   }

   // Construct an attribute handle set
   RTI::AttributeHandleSet* hSet = 
      RTI::AttributeHandleSetFactory::create(theAttrNameHandleMap.size());
   for (DtAttrNameHandleMap::iterator iter = theAttrNameHandleMap.begin();
        iter != theAttrNameHandleMap.end();
        iter++)
   {
      hSet->add(iter->second);
   }

    // Publish and subscribe
   int cnt=0;
   try
   {
   rtiAmb.publishObjectClass(theClassHandle, *hSet);
   rtiAmb.tick(0.1, 0.2);
   cnt=1;
   rtiAmb.subscribeObjectClassAttributes(theClassHandle, *hSet);
   rtiAmb.tick(0.1, 0.2);
   }
   catch (RTI::Exception& ex)
   {
      cout  << "RTI Exception: "
            << ex._name << " "
            << ex._reason << endl
            << "Could not "
            << (cnt ? "publish" : "subscribe") << endl;
      delete hSet;
      return false;
   }

   std::string objectName("Talk");

   // Register the object instance
   try
   {
      unsigned int objId = abs(getpid());
      std::stringstream pid;
      pid << objId;
      objectName += pid.str();
      theObjectHandle = rtiAmb.registerObjectInstance(theClassHandle,
            objectName.c_str());
      //theObjectHandle = rtiAmb.registerObjectInstance(theClassHandle);

      // Add name-handle to map
      theAmbData.objectInstanceMap[theObjectHandle] =
         rtiAmb.getObjectInstanceName(theObjectHandle);

      rtiAmb.tick(0.1, 0.2);
   }
   catch (RTI::Exception& ex)
   {
      cout  << "RTI Exception: "
            << ex._name << " "
            << ex._reason << endl
            << "Could not  Register Object "
            << objectName.c_str()
            << " with class "
            << theClassName.c_str() << endl;
      delete hSet;
      return false;
   }

   cout  << "Registered object "
         << objectName.c_str()
         << " with class name "
         <<  theClassName.c_str() << endl;
      delete hSet;
   return true;
}


////////////////////////////////////////////////////////////////////////////////
// Publish and Subscribe to an interaction class
bool publishAndSubscribeInteraction(RTI::RTIambassador & rtiAmb)
{
   // Get the interaction class handle
   try
   {  
      theInterClassHandle = rtiAmb.getInteractionClassHandle(theInterClassName.c_str());
      theAmbData.interactionClassMap[theInterClassHandle] = theInterClassName;
   }
   catch (RTI::Exception& ex)
   {
      cout  << "RTI Exception: "
            << ex._name << " "
            << ex._reason << endl
            << "Could not get interaction class handle: "
            << theInterClassName.c_str() << endl;
      return false;
   }

   // Get the parameter handles and construct the name-handle map
   std::string paramName;
   try
   {
      paramName  = "EventIdentifier";
      theParamNameHandleMap[paramName] = 
            rtiAmb.getParameterHandle(paramName.c_str(), theInterClassHandle);
      paramName  = "FireControlSolutionRange";
      theParamNameHandleMap[paramName] = 
            rtiAmb.getParameterHandle(paramName.c_str(), theInterClassHandle);
      paramName  = "FireMissionIndex";
      theParamNameHandleMap[paramName] = 
            rtiAmb.getParameterHandle(paramName.c_str(), theInterClassHandle);
      paramName  = "FiringLocation";
      theParamNameHandleMap[paramName] = 
            rtiAmb.getParameterHandle(paramName.c_str(), theInterClassHandle);
      paramName  = "FiringObjectIdentifier";
      theParamNameHandleMap[paramName] = 
            rtiAmb.getParameterHandle(paramName.c_str(), theInterClassHandle);
      paramName  = "FuseType";
      theParamNameHandleMap[paramName] = 
            rtiAmb.getParameterHandle(paramName.c_str(), theInterClassHandle);
      paramName  = "InitialVelocityVector";
      theParamNameHandleMap[paramName] = 
            rtiAmb.getParameterHandle(paramName.c_str(), theInterClassHandle);
      paramName  = "MunitionObjectIdentifier";
      theParamNameHandleMap[paramName] = 
            rtiAmb.getParameterHandle(paramName.c_str(), theInterClassHandle);
      paramName  = "MunitionType";
      theParamNameHandleMap[paramName] = 
            rtiAmb.getParameterHandle(paramName.c_str(), theInterClassHandle);
      paramName  = "QuantityFired";
      theParamNameHandleMap[paramName] = 
            rtiAmb.getParameterHandle(paramName.c_str(), theInterClassHandle);
      paramName  = "RateOfFire";
      theParamNameHandleMap[paramName] = 
            rtiAmb.getParameterHandle(paramName.c_str(), theInterClassHandle);
      paramName  = "TargetObjectIdentifier";
      theParamNameHandleMap[paramName] = 
            rtiAmb.getParameterHandle(paramName.c_str(), theInterClassHandle);
      paramName  = "WarheadType";
      theParamNameHandleMap[paramName] = 
            rtiAmb.getParameterHandle(paramName.c_str(), theInterClassHandle);
   }
   catch (RTI::Exception& ex)
   {
      cout  << "RTI Exception: "
            << ex._name << " "
            << ex._reason << endl
            << "Could not get parameter handle "
            << paramName.c_str() << endl;
      return false;
   }

    // Publish and subscribe
   int cnt=0;
   try
   {
   rtiAmb.publishInteractionClass(theInterClassHandle);
   rtiAmb.tick(0.1, 0.2);
   cnt=1;
   rtiAmb.subscribeInteractionClass(theInterClassHandle);
   rtiAmb.tick(0.1, 0.2);
   }
   catch (RTI::Exception& ex)
   {
      cout  << "RTI Exception: "
            << ex._name << " "
            << ex._reason << endl
            << "Could not "
            << (cnt ? "publish" : "subscribe")
            << " to interaction." << endl;
      return false;
   }

   cout  << "Subscribed to interaction class: "
         << theInterClassName.c_str()
         << " with handle: "
         <<  theInterClassHandle << endl;
   return true;
}

int main(int argc, char** argv)
{
   try
   {
      // Federate and Federation info
      std::string federationName("MAKsimple");
      std::string federationFile("MAKsimple.fed");
      std::string federateType("rtisimple13");

      if (argc > 1)
      {
         if (strcmp(argv[1], "-h") == 0)
         {
            cout  << "Usage: talk1516 [federtionName] [federationFile] [federateType]" << endl;
            cout  << "default values: "
                  << "\"" << federationName.c_str() << "\" "
                  << "\"" << federationFile.c_str() <<  "\" "
                  << "\"" << federateType.c_str() <<  "\" " << endl;
            return 0;
         }
         federationName = argv[1];
      }

      if (argc > 2)
      {
         federationFile = argv[2];
      }

      if (argc > 3)
      {
         federateType = argv[3];
      }

      // RTI and Federate Ambassadors
      RTI::RTIambassador rtiAmb;
      MyFederateAmbassador fedAmb(theAmbData);
   
      // Create the federation
      createFedEx(rtiAmb, federationName, federationFile);

      // Join the federation
      joinFedEx(rtiAmb, &fedAmb, federateType, federationName);

      // Publish, subscribe and register and object
      if (!publishSubscribeAndRegisterObject(rtiAmb))
      {
         resignAndDestroy(rtiAmb, federationName);
         return 0;
      }

      // Publish and subscribe to the required interaction
      if (!publishAndSubscribeInteraction(rtiAmb))
      {
         resignAndDestroy(rtiAmb, federationName);
         return 0;
      }
      
      // Construct an attribute handle value pair set with the
      // values containing the attribute names
      RTI::AttributeHandleValuePairSet *attrValues =
         RTI::AttributeSetFactory::create(theAttrNameHandleMap.size());

      for (DtAttrNameHandleMap::iterator iter = theAttrNameHandleMap.begin();
           iter != theAttrNameHandleMap.end();
           iter++)
      {
         // Attribute values will be just the name of the attribute.
         attrValues->add(iter->second,
           iter->first.c_str(), iter->first.length()+1);
      }

      // Construct a parameter handle value pair set with the
      // values containing the parameter names
      RTI::ParameterHandleValuePairSet *paramValues =
         RTI::ParameterSetFactory::create(theParamNameHandleMap.size());

      for (DtParamNameHandleMap::iterator iter2 = theParamNameHandleMap.begin();
           iter2 != theParamNameHandleMap.end();
           iter2++)
      {
         // Parameter values will be just the name of the attribute.
         paramValues->add(iter2->second,
           iter2->first.c_str(), iter2->first.length()+1);
      }

   
#ifdef WIN32
      WSADATA data;
      WSAStartup(MAKEWORD(1,1), &data);
#endif
      rtiAmb.tick();
      long count=0;
      while (1)
      {
         std::stringstream ss;
         ss << "1.3-" << count++;
         std::string tag(ss.str());

         // Update the object
         rtiAmb.updateAttributeValues(
            theObjectHandle,
            *attrValues,
            tag.c_str());

         // Send an interaction every few passes
         if ( count % 5 == 0 )
         {
            rtiAmb.sendInteraction(
               theInterClassHandle,
               *paramValues,
               tag.c_str());
         }

         rtiAmb.tick(0.1, 0.5);
         
         if (input.keybrdTick() < 0)
            break;
         
#ifdef WIN32
         Sleep(2000);
#else
         sleep(2);
#endif
      }

      // Resign and destroy federation
      resignAndDestroy(rtiAmb, federationName);
   }
   catch (RTI::Exception& ex)
   {
      cout  << "RTI Exception (main loop): "
            << ex._name << " "
            << ex._reason << endl;
   }
#ifdef WIN32
   WSACleanup();
#endif

   return 0;

}

